﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Web;
using System.Globalization;
using System.IO;
using System.Net;
using A2DFramework.HttpLoadBalancer.Utility;
using A2DFramework.HttpLoadBalancer.HttpProtocal;
using A2DFramework.HttpLoadBalancer.Parsers;
using A2DFramework.HttpLoadBalancer.Parsers.ConcreteParsers;
using System.Threading;

namespace A2DFramework.HttpLoadBalancer.Connection
{
    public class ConnectionWorker:IDisposable
    {
        private readonly MemoryStream _responseContent;
        private Socket socket;

        public string ContentServerUrl { get; set; }

        public Guid Id { get; private set; }

        public ConnectionWorker(Socket socket)
        {
            Id = Guid.NewGuid();
            _responseContent = new MemoryStream();
            this.socket = socket;
        }
        public void SetSocket(Socket socket)
        {
            this.socket = socket;
        }

        public void Prepare()
        { 
        }
        public void ShutDown()
        {
        }

        public void Dispose()
        {
            ConnectionWorkerPool.ReturnToPool(this);
        }




        public void ProcessRequest(object state)
        {
            if (SocketHelper.WaitForRequestBytes(this.socket) == 0)
            {
                HttpResponseEntity error = new HttpResponseEntity();
                error.ResponseLine = new ResponseStatusLine() { 
                    StatusCode=500,
                    StatusDescription="500 Error",
                    Version = HttpProtocalVersions.HTTP11
                };
                WriteEntireResponse(error);
                this.Dispose();
                return;
            }

            byte[] firstData;
            int retryCount = 0;
            receiveBuffer:
            firstData= SocketHelper.ReadRequestBytes(socket, SocketHelper.maxBufferBytes);
            if (firstData == null)
            {
                retryCount++;
                Thread.Sleep(300);
                if (retryCount > 50)
                {
                    Console.WriteLine("超出50次重试");
                    this.Dispose();
                    return;
                }
                goto receiveBuffer;
            }
            string receivedMsg = System.Text.Encoding.UTF8.GetString(firstData);

            HttpRequestEntity requestInfo = Parsers.Parsers.HttpRequestParser.Parse(receivedMsg);
            if (requestInfo.RequestLine.Action == HttpRequestActions.POST
                || requestInfo.RequestLine.Action == HttpRequestActions.PUT)
            {
                byte[] deepData = SocketHelper.ReadRequestBytes(socket, SocketHelper.maxBufferBytes);
                byte[] bodyBytes = Helper.GetHttpBodyBytes(firstData, deepData);
                requestInfo.BodyBytes = bodyBytes;
            }
            Console.WriteLine("RequestInfo.RequestLine: {0} {1} {2}", requestInfo.RequestLine.Action, requestInfo.RequestLine.Path, requestInfo.RequestLine.Version);

            //send to content servers
            ContentServerForwarder agent = new ContentServerForwarder(this.ContentServerUrl, requestInfo);

            //receive html
            HttpProtocal.HttpResponseEntity response = agent.GetResponse();
            if (response != null)
            {
                Console.WriteLine("response.Response Line: {0} {1} {2}", response.ResponseLine.StatusCode, response.ResponseLine.StatusDescription, response.ResponseLine.Version);

                //send to request socket
                this.WriteEntireResponse(response);
                Console.WriteLine("processed");
            }
            
            this.Dispose();
        }


        public void WriteEntireResponse(HttpResponseEntity package)
        {
            bool keepAlive = false;
            if (package.HttpHeaders.Connection != null
                && package.HttpHeaders.Connection.HasValue
                && package.HttpHeaders.Connection.Value.Value!=string.Empty
                && package.HttpHeaders.Connection.Value.Value.Equals("keep-alive", StringComparison.OrdinalIgnoreCase))
                keepAlive = true;

            try
            {
                string sheaders = MakeResponseHeaders(package);

                List<byte> data = new List<byte>();
                data.AddRange(Encoding.UTF8.GetBytes(sheaders));
                if (package.BodyBytes!=null)
                    data.AddRange(package.BodyBytes);
                socket.Send(data.ToArray());
            }
            //catch (SocketException)
            //{
            //}
            finally
            {
                if (!keepAlive)
                {
                    Close();
                }
            }
        }

        private static string MakeResponseHeaders(HttpResponseEntity package)
        {
            if (package.HttpHeaders.GetHeader("LBServer") == null)
                package.HttpHeaders.SetHeader("LBServer", "A2D");
            package.HttpHeaders.SetHeader("LBDate", DateTime.Now.ToUniversalTime().ToString("R", DateTimeFormatInfo.InvariantInfo));
            if (package.HttpHeaders.TransferEncoding != null && package.HttpHeaders.TransferEncoding.Value.Value!=string.Empty)
                package.HttpHeaders.RemoveHeader("Content-Length");

            StringBuilder sb = new StringBuilder();

            sb.Append(Helper.ConvertHttpVersion(package.ResponseLine.Version) + " " + package.ResponseLine.StatusCode + " " + package.ResponseLine.StatusDescription + Consts.CRLF);
            sb.Append(Helper.ConvertHttpHeaders(package.HttpHeaders));
            sb.Append(Consts.CRLF);

            return sb.ToString();
        }

        public void Close()
        {
            try
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }
            // ReSharper disable EmptyGeneralCatchClause
            catch
            // ReSharper restore EmptyGeneralCatchClause
            {
            }
            finally
            {
                socket = null;
            }
        }
    }
}
